package org.vaadin.nf4v.core.navigation;

import com.vaadin.ui.Component;
import org.vaadin.nf4v.core.NavigationException;
import org.vaadin.nf4v.core.navigation.event.NavigationState;
import org.vaadin.nf4v.core.navigation.target.NavigationTarget;
import org.vaadin.nf4v.core.util.Params;

/**
 * NavigationRequest implementation.
 */
public class NavigationRequestImpl implements NavigationRequest {
    private NavigationState state;
    private NavigationTarget target;
    private Component pageInstance;
    private boolean cancelled = false;
    private boolean parametersChangeRequest;
    private RequestInitiatorType requestInitiatorType;
    private Params params;

    // todo request attributes ?

    private boolean cancellationImpossible;

    public NavigationRequestImpl(NavigationTarget target, NavigationState state, RequestInitiatorType type) {
        this.target = target;
        setState(state);
        requestInitiatorType = type;
        params = new Params(target);
    }

    /**
     * Request state represents navigation phase. On each phase all listeners are notified with request instance.
     *
     * @return request state
     */
    public NavigationState getState() {
        return state;
    }

    void setState(NavigationState state) {
        this.state = state;
        if (state == NavigationState.PAGE_NAVIGATION || state == NavigationState.PAGE_PARAMETERS_CHANGE) {
            parametersChangeRequest = state == NavigationState.PAGE_PARAMETERS_CHANGE;
        }
        cancellationImpossible = state.ordinal() >= NavigationState.BEFORE_NAVIGATION.ordinal();
    }

    /**
     * @return request initiator (URI or API)
     */
    public RequestInitiatorType getRequestInitiatorType() {
        return requestInitiatorType;
    }

    /**
     * @return target object
     */
    public NavigationTarget getTarget() {
        return target;
    }

    /**
     * Note: page instance is available only after PAGE_INSTANTIATED state
     *
     * @return page instance
     */
    public Component getPageInstance() {
        return pageInstance;
    }

    void setPageInstance(Component pageInstance) {
        this.pageInstance = pageInstance;
    }

    /**
     * Note: correct value will be set only after reaching PAGE_NAVIGATION or PAGE_PARAMETERS_CHANGE state
     *
     * @return true if request is parameters change request
     */
    public boolean isParametersChangeRequest() {
        return parametersChangeRequest;
    }

    /**
     * Cancels request.
     * Be careful, if initial home page request will be interrupted and no other request will be performed
     * in listener, no page will be shown!
     * <p/>
     * It in not possible to cancel request after BEFORE_NAVIGATION state
     */
    public void cancel() {
        if (cancellationImpossible) {
            throw new NavigationException("Request can't be cancelled in state " + state);
        }
        cancelled = true;
    }

    /**
     * @return true if request is cancelled, false otherwise
     */
    public boolean isCancelled() {
        return cancelled;
    }

    /**
     * @return params utility, which provides easy access to parsed parameters inside target object
     */
    public Params params() {
        return params;
    }

    @Override
    public String toString() {
        return "NavigationRequest{" +
                "cancelled=" + cancelled +
                ", pageInstance=" + pageInstance +
                ", parametersChangeRequest=" + parametersChangeRequest +
                ", requestInitiatorType=" + requestInitiatorType +
                ", state=" + state +
                ", target=" + target +
                '}';
    }
}
